// /////////////////////////////////////////////////////////////////////////////
//
// Name:            MeshComponent.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           Component wrapper of Irrlicht's IMeshSceneNode.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "MeshComponent.h"
#include "../../core/GameManager.h"


// MeshComponent class
// MeshComponent constructor (default).
MeshComponent::MeshComponent(Entity *parent)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
      init();
}

// MeshComponent constructor.
MeshComponent::MeshComponent(Entity *parent, const std::string &fileName, const vector3df &scale)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
    {
       init(scale);
       setMesh(fileName);
    }
}

// MeshComponent constructor.
MeshComponent::MeshComponent(Entity *parent, IMesh *mesh, const vector3df &scale)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
    {
       init(scale);
       setMesh(mesh);
    }
}

// MeshComponent constructor (derivatives).
MeshComponent::MeshComponent(Entity *parent, bool isDerived)
: SceneComponent(parent, true)
{
}

// MeshComponent deconstructor.
MeshComponent::~MeshComponent()
{
    if(pParent != NULL)
    {
       mMeshSN->remove();
       mSceneNode = NULL;
    }
}

// Common initialisation method.
void MeshComponent::init(const vector3df &scale)
{
    // Get pointers to needed sub-systems of the Game Manager.
    ISceneManager *pSceneMgr = GameManager::Instance()->getSceneManager();

    // Create the internal scene node.
    mMeshSN = pSceneMgr->addMeshSceneNode(0, 0, pParent->getID(), pParent->getAbsolutePosition(),
                                          pParent->getAbsoluteRotation(), scale, true);
    mSceneNode = mMeshSN;

    // Set-up internal mechanism for collision detection.
    mTriSelector = pSceneMgr->createTriangleSelectorFromBoundingBox(mMeshSN);
    mMetaSelector = pSceneMgr->createMetaTriangleSelector();
}

// Returns a direct pointer to the IMeshSceneNode.
IMeshSceneNode* MeshComponent::getMeshSceneNode()
{
    return mMeshSN;
}

// Get the currently defined mesh for display.
IMesh* MeshComponent::getMesh()
{
    return mMeshSN->getMesh();
}

// Loads and sets a new mesh to display.
void MeshComponent::setMesh(const std::string &fileName)
{
    // Set up variables.
    AssetGroup *assets = pParent->getAssetGroup();
    IMesh *mesh = 0;

    // Retrieve pointer to the mesh.
    if(assets != NULL)
    {
        // Unsubscribe from previous asset.
        assets->disconnectEventSignal(std::string("meshes/") + mMeshFileName, this);

        // Get mesh.
        MeshProcessor *processor = static_cast<MeshProcessor*>(assets->getAssetProcessor("meshes"));
        mesh = processor->getMesh(fileName);

        if(mesh)
        {
           assets->connectEventSignal(std::string("meshes/") + fileName, this,
                                        &MeshComponent::onMesh);
           mMeshFileName = fileName;
        }
    }

    else
       mesh = GameManager::Instance()->getSceneManager()->getMesh(fileName.c_str());

    // Set mesh.
    mMeshSN->setMesh(mesh);
}

// Sets a new mesh to display.
void MeshComponent::setMesh(IMesh *mesh)
{
    // Check if we need to unsubscribe from an asset group mesh.
    AssetGroup *assets = pParent->getAssetGroup();

    if(assets != NULL)
    {
       assets->disconnectEventSignal(std::string("meshes/") + mMeshFileName, this);
       mMeshFileName = "";
    }

    // Set mesh.
    mMeshSN->setMesh(mesh);
}

// Responds to changes of the mesh attached to this component.
void MeshComponent::onMesh(void *p)
{
    // Get pointer to the new mesh.
    IMesh *mesh = reinterpret_cast<IMesh*>(p);

    // Set new mesh.
    mMeshSN->setMesh(mesh);
}

// Serializes this object.
bool MeshComponent::onSerialization(SerializedAttributes *attributes)
{
    // Create a root.
    SerializedAttributes *root = new SerializedAttributes();

    // Serialize attributes inherited from SceneComponent.
    SceneComponent::onSerialization(root);

    // Serialize attributes.
    root->pushString("mesh", mMeshFileName);

    // Add root to the given collection of attributes.
    attributes->pushObject("MeshComponent", root);
    root->drop();

    // Return
    return true;
}

// Deserializes this object.
bool MeshComponent::onDeserialization(SerializedAttributes *attributes)
{
    // Retrieve root attribute from the collection.
    SerializedAttribute *rootAttribute = attributes->pullAttribute();

    if(rootAttribute == NULL || rootAttribute->getName() != "MeshComponent")
      return false;

    SerializedAttributes *root = reinterpret_cast<SerializedAttributes*>(rootAttribute->getObject());
    root->grab();

    rootAttribute->drop(); // We no longer need this attribute.

    // Process all attributes in the root collection.
    for(SerializedAttribute *attribute = root->pullAttribute(); attribute != NULL; attribute = root->pullAttribute())
    {
       // Deserialize attributes
       if(attribute->getName() == "SceneComponent")
       {
           // Rewrap attribute so that it can be deserialized properly.
           SerializedAttributes *wrappedAttribute = new SerializedAttributes();
           wrappedAttribute->pushAttribute(attribute);

           // Deserialize the component.
           SceneComponent::onDeserialization(wrappedAttribute);

           // Clean up.
           wrappedAttribute->drop();
       }

       else if(attribute->getName() == "mesh")   setMesh(attribute->getString());

       // We no longer need a reference to the attribute.
       attribute->drop();
    }

    // We no longer need a reference to the root.
    root->drop();

    // We're done.
    return true;
}

// End of File
